<?php

namespace core\system;

use core\util\TextUtil;
use ReflectionClass;

class Build {

    private $rootPath = 'application/controller';
    private $fileConfig = '.htaccess';
    private $file = ["\web" => "index.php", "\cp" => "cp.php", "\service" => "index.php", "\cpservice" => "cp.php"];

    public function __construct() {
        
    }

    public function run() {
        $rewriteRule = $this->filter();
        $this->createFileConfig($rewriteRule);
    }

    public function createFileConfig($rewriteRule = array()) {
        $content = "Options +FollowSymLinks \n";
        $content .= "RewriteEngine On \n";
        $content .= "\n";
        foreach ($rewriteRule as $rR) {
            $content .= "RewriteRule ^" . $rR[0] . "$ " . $rR[1] . " [L] \n";
        }
        $fp = fopen($this->fileConfig, "w");
        fwrite($fp, $content);
        fclose($fp);
    }

    public function filter() {
        $files = TextUtil::read_all_files($this->rootPath);
        $namespace = [];
        $rewriteRule = [];
        foreach ($files['files'] as $file) {
            require_once $file;
            $phpFile = $this->readClass($file);
            foreach ($phpFile as $nspace => $className) {
                $cl = @$namespace[$nspace];
                if ($cl == null) {
                    $cl = [];
                }
                $namespace[$nspace] = array_merge($cl, $className);
            }
        }
        foreach ($namespace as $nspace => $classNames) {
            $file = explode("controller", $nspace);
            $file = end($file);
            foreach ($classNames as $className) {
                $class = new ReflectionClass($nspace . "\\" . $className);
                foreach ($class->getMethods() as $method) {
                    $mapping = $this->getMapping($method->getDocComment());
                    if ($mapping != null) {
                        $rewriteRule[] = $this->genRewriteRule($mapping, $file, $class->getName(), $method->getName());
                    }
                }
            }
        }
        return $rewriteRule;
    }

    private function genRewriteRule($mapping, $file, $class, $method) {
        $mapping = explode(",", $mapping);
        $rl = [];
        if (count($mapping) == 1) {
            $rl[0] = $this->rewriteRule($mapping[0]);
            $rl[1] = $this->file[$file] . '?mod=' . $this->module($class) . '&action=' . $method . ($file == '\cpservice' ? '&service=true' : '');
        } else {
            $rl[0] = $this->rewriteRule($mapping[0]);
            $rl[1] = $this->file[$file] . '?mod=' . $this->module($class) . '&action=' . $method . $this->genParam($mapping) . ($file == '\cpservice' ? '&service=true' : '');
        }
        return $rl;
    }

    private function genParam($mapping) {
        unset($mapping[0]);
        $qString = '';
        $mapping = implode(",", $mapping);
        preg_match('/param=\[(.*)\]/', $mapping, $matches);
        if ($matches[1] != '') {
            $matches = explode(",", $matches[1]);
            foreach ($matches as $param) {
                $qString .= "&" . $param . "=$" . (array_search($param, $matches) + 1);
            }
        }
        return $qString;
    }

    private function rewriteRule($mapping) {
        $mapping = explode("=", $mapping);
        $mapping = $mapping[1];
        preg_match_all('/\{([a-zA-Z]+)}/', $mapping, $matches);
        if (count($matches[0]) > 0) {
            foreach ($matches[0] as $param) {
                $mapping = str_replace($param, "(.*)", $mapping);
            }
        }
        return $mapping;
    }

    private function module($class) {
        $class = explode("\\", $class);
        $class = explode("Controller", end($class));
        return strtolower($class[0]);
    }

    private function readClass($file) {
        $class = [];
        if (file_exists($file)) {
            $php_code = file_get_contents($file);
            $class = $this->get_php_classes($php_code);
        }
        return $class;
    }

    private function get_php_classes($phpcode) {
        $classes = array();

        $namespace = 0;
        $tokens = token_get_all($phpcode);
        $count = count($tokens);
        $dlm = false;
        for ($i = 2; $i < $count; $i++) {
            if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] == "phpnamespace" || $tokens[$i - 2][1] == "namespace")) ||
                    ($dlm && $tokens[$i - 1][0] == T_NS_SEPARATOR && $tokens[$i][0] == T_STRING)) {
                if (!$dlm)
                    $namespace = 0;
                if (isset($tokens[$i][1])) {
                    $namespace = $namespace ? $namespace . "\\" . $tokens[$i][1] : $tokens[$i][1];
                    $dlm = true;
                }
            } elseif ($dlm && ($tokens[$i][0] != T_NS_SEPARATOR) && ($tokens[$i][0] != T_STRING)) {
                $dlm = false;
            }
            if (($tokens[$i - 2][0] == T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == "phpclass")) && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) {
                $class_name = $tokens[$i][1];
                if (!isset($classes[$namespace]))
                    $classes[$namespace] = array();
                $classes[$namespace][] = $class_name;
            }
        }
        return $classes;
    }

    private function getMapping($string = null) {
        preg_match('/\@mapping\((.*)\)/', $string, $matches);
        return @$matches[1];
    }

}
